package com.quanyan.alipay.util;


import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.quanyan.alipay.config.AlipayConfig;
import com.quanyan.alipay.sign.MD5;
import com.quanyan.alipay.sign.RSA;
import com.quanyan.common.http.HttpClientUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map;


/* *
 *类名：AlipayNotify
 *功能：支付宝通知处理类
 *详细：处理支付宝各接口通知返回
 *版本：3.3
 *日期：2012-08-17
 *说明：
 *以下代码只是为了方便商户测试而提供的样例代码，商户可以根据自己网站的需要，按照技术文档编写,并非一定要使用该代码。
 *该代码仅供学习和研究支付宝接口使用，只是提供一个参考

 *************************注意*************************
 *调试通知返回时，可查看或改写log日志的写入TXT里的数据，来检查通知返回是否正常
 */
public class AlipayNotify {

    private static Logger logger = LoggerFactory.getLogger(AlipayNotify.class);

    /**
     * 支付宝消息验证地址
     */
    private static final String HTTPS_VERIFY_URL = "https://mapi.alipay.com/gateway.do?service=notify_verify&";

    /**
     * 验证消息是否是支付宝发出的合法消息
     *
     * @param params 通知返回来的参数数组
     * @return 验证结果
     */
    public static boolean verify(Map<String, String> params, AlipayConfig alipayConfig) {

        //判断responsetTxt是否为true，isSign是否为true
        //responsetTxt的结果不是true，与服务器设置问题、合作身份者ID、notify_id一分钟失效有关
        //isSign不是true，与安全校验码、请求时的参数格式（如：带自定义参数等）、编码格式有关

        String responseTxt = "true";
        if (params.get("notify_id") != null) {
            String notify_id = params.get("notify_id");
            responseTxt = verifyResponse(notify_id, alipayConfig);

        }
        String sign = "";
        if (params.get("sign") != null) {
            sign = params.get("sign");
        }
        boolean isSign = getSignVeryfy(params, sign, alipayConfig);
        return isSign && responseTxt.equals("true");
    }

    /**
     * 验证消息是否是支付宝发出的合法消息
     *
     * @param params 通知返回来的参数数组
     * @return 验证结果
     */
    public static String verifyDB(Map<String, String> params, AlipayConfig alipayConfig) {

        //判断responsetTxt是否为true，isSign是否为true
        //responsetTxt的结果不是true，与服务器设置问题、合作身份者ID、notify_id一分钟失效有关
        //isSign不是true，与安全校验码、请求时的参数格式（如：带自定义参数等）、编码格式有关
        String responseTxt = "true";
        if (params.get("notify_id") != null) {
            String notify_id = params.get("notify_id");
            responseTxt = verifyResponse(notify_id, alipayConfig);
        }
        String sign = "";
        if (params.get("sign") != null) {
            sign = params.get("sign");
        }
        boolean isSign = getSignVeryfyMD5(params, sign, alipayConfig);

        //写日志记录（若要调试，请取消下面两行注释）
        String sWord = "responseTxt=" + responseTxt + "\n isSign=" + isSign + "\n 返回回来的参数：" + AlipayCore.createLinkString(params);

        if (isSign && responseTxt.equals("true")) {
            return sWord;
        } else {
            return null;
        }
    }

    /**
     * 根据反馈回来的信息，生成签名结果
     *
     * @param Params 通知返回来的参数数组
     * @param sign   比对的签名结果
     * @return 生成的签名结果
     */
    private static boolean getSignVeryfyMD5(Map<String, String> Params, String sign, AlipayConfig alipayConfig) {
        //过滤空值、sign与sign_type参数
        Map<String, String> sParaNew = AlipayCore.paraFilterContainSignType(Params);
        //获取待签名字符串
        String preSignStr = AlipayCore.createLinkString(sParaNew);
        //获得签名验证结果
        return MD5.verify(preSignStr, sign, alipayConfig.getKey(), alipayConfig.getInput_charset());
    }

    /**
     * 根据反馈回来的信息，生成签名结果
     *
     * @param Params 通知返回来的参数数组
     * @param sign   比对的签名结果
     * @return 生成的签名结果
     */
    private static boolean getSignVeryfy(Map<String, String> Params, String sign, AlipayConfig alipayConfig) {
        //过滤空值、sign与sign_type参数
        Map<String, String> sParaNew = AlipayCore.paraFilterContainSignType(Params);
        //获取待签名字符串
        String preSignStr = AlipayCore.createLinkString(sParaNew);
        //获得签名验证结果
        boolean isSign = false;
        if (alipayConfig.getSign_type().equals("RSA")) {
            isSign = RSA.verify(preSignStr, sign, alipayConfig.getAli_public_key(), alipayConfig.getInput_charset());
            //2.0接口是需要开放平台public_key验证签名的
            if(false == isSign){
                isSign = RSA.verify(preSignStr, sign, alipayConfig.getOpen_public_key(), alipayConfig.getInput_charset());
            }
        }
        if (alipayConfig.getSign_type().equals("MD5")){
            isSign = MD5.verify(preSignStr, sign, alipayConfig.getKey(), alipayConfig.getInput_charset());
        }
        Gson gson  = new GsonBuilder().setPrettyPrinting().create();
        logger.info("Verify Sign Params = " + preSignStr);
        logger.info("Vefify Sign sign = " + sign);
        logger.info("Verify Sign Result = " + isSign);
        return isSign;
    }

    /**
     * 获取远程服务器ATN结果,验证返回URL
     *
     * @param notify_id 通知校验ID
     * @return 服务器ATN结果
     * 验证结果集：
     * invalid命令参数不对 出现这个错误，请检测返回处理中partner和key是否为空
     * true 返回正确信息
     * false 请检查防火墙或者是服务器阻止端口问题以及验证时间是否超过一分钟
     */
    public static String verifyResponse(String notify_id, AlipayConfig alipayConfig){
        //获取远程服务器ATN结果，验证是否是支付宝服务器发来的请求

        String partner = alipayConfig.getPartner();
        String veryfy_url = HTTPS_VERIFY_URL + "partner=" + partner + "&notify_id=" + notify_id;

        String result =  checkUrl(veryfy_url);
        logger.info("Validate is Alipay Request Result : notify_id = " + notify_id + ", result  =" + result);
        return result;
    }

    /**
     * 获取远程服务器ATN结果
     *
     * @param urlvalue 指定URL路径地址
     * @return 服务器ATN结果
     * 验证结果集：
     * invalid命令参数不对 出现这个错误，请检测返回处理中partner和key是否为空
     * true 返回正确信息
     * false 请检查防火墙或者是服务器阻止端口问题以及验证时间是否超过一分钟
     */
    public static String checkUrl(String urlvalue) {
        String inputLine = null;
        HttpURLConnection urlConnection = null;
        BufferedReader in = null;
        InputStreamReader inputStreamReader = null;
        InputStream inputStream = null;
        try {
            URL url = new URL(urlvalue);
            urlConnection = (HttpURLConnection) url.openConnection();
            inputStream = urlConnection.getInputStream();
            inputStreamReader = new InputStreamReader(inputStream);
            in = new BufferedReader(inputStreamReader);
            inputLine = in.readLine();
        } catch (Exception e) {
            inputLine = "";
        } finally {
            try {
                if (in != null)
                    in.close();
            } catch (IOException e) {
            }
            if (inputStreamReader != null){
                try {
                    inputStreamReader.close();
                } catch (IOException e) {
                }
            }
            if (inputStream != null){
                try {
                    inputStream.close();
                } catch (IOException e) {
                }
            }
            if (urlConnection != null) {
                urlConnection.disconnect();
            }
        }
        return inputLine == null ? "" : inputLine;
    }
}
